#!/usr/bin/python
# -*- coding: utf-8 -*-
"""All are decorator!!!
"""

class ArgumentLengthError(Exception):
    """"""
    
class ArgumentTypeError(Exception):
    """Accepts argument type error."""

class ReturnValueError(Exception):
    """Return value error."""

def accepts(*types):
    """参数类型校验装饰器
    
    若需要装饰的方法是类方法，则必须符合python的规范，类方法第一个参数的名称必须为self
    """
    def check_accepts(f):
        is_class_method = False
        want_len, now_len = len(types), f.func_code.co_argcount
        if now_len - want_len == 1 and f.func_code.co_varnames[0] in ('self', 'cls'): # fixed class method
            now_len -= 1
            is_class_method = True
        if want_len != now_len:
            raise ArgumentLengthError('want %d args, but now %d' % \
                                      (want_len, now_len))
        def new_f(*args, **kwds):
            if is_class_method:
                check_args = args[1:]
            else:
                check_args = args
            for (a, t) in zip(check_args, types):
                if not isinstance(a, t):
                    raise ArgumentTypeError("arg %r does not match %s" % \
                                            (a, t))
            return f(*args, **kwds)
        if hasattr(f, 'func_name'):
            new_f.func_name = f.func_name
        new_f.__doc__ = f.__doc__
        return new_f
    return check_accepts

def returns(rtype):
    """返回值类型校验装饰器"""
    def check_returns(f):
        def new_f(*args, **kwds):
            result = f(*args, **kwds)
            if not isinstance(result, rtype):
                raise ReturnValueError(
                    "return value %r does not match %s" % (result,rtype))
            return result
        if hasattr(f, 'func_name'):
            new_f.func_name = f.func_name
        new_f.__doc__ = f.__doc__
        return new_f
    return check_returns

def synchronized(lock):
    """锁同步装饰方法
    
    lock必须实现了acquire和release方法
    
    可以使用with语法来取代此装饰方法
    """
#    assert hasattr(lock, 'acquire1') and hasattr(lock, 'release') and \
#        callable(lock.acquire) and callable(lock.release), \
#            '%r must implement acquire() and release()' % lock
    def sync_with_lock(f):
        def new_f(*args, **kwargs):
            lock.acquire()
            try:
                return f(*args, **kwargs)
            finally:
                lock.release()
        if hasattr(f, 'func_name'):
            new_f.func_name = f.func_name
        new_f.__doc__ = f.__doc__
        return new_f
    return sync_with_lock